package com.bjsxt.match.impl;

import com.bjsxt.enums.OrderDirection;
import com.bjsxt.match.MatchService;
import com.bjsxt.match.MatchServiceFactory;
import com.bjsxt.match.MatchStrategy;
import com.bjsxt.model.*;
import com.bjsxt.rocket.Source;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Service;
import org.springframework.util.MimeTypeUtils;
import sun.net.www.MessageHeader;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class LimitPriceMatchServiceImpl implements MatchService, InitializingBean {

    @Autowired
        private Source source;

    @Override
    public void match(OrderBooks orderBooks, Order order) {

        if (order.isCancelOrder()){
            orderBooks.cancelOrder(order);
            Message<String> build = MessageBuilder.withPayload(order.getOrderId()).build();
            source.cancelorderout().send(build);

            return;
        }


        log.info("开始撮合");

        if(order.getPrice().compareTo(BigDecimal.ZERO)<=0){
            return;
        }

        Iterator<Map.Entry<BigDecimal, MergeOrder>> marketQueueIterator = null;
        if(order.getOrderDirection() == OrderDirection.BUY.getCode()){
                    marketQueueIterator = orderBooks.getCurrentLimitPriceIterator(OrderDirection.SELL);
        }else
        {
            marketQueueIterator = orderBooks.getCurrentLimitPriceIterator(OrderDirection.BUY);
        }


        boolean exitLoop = false;

        List<Order> completedOrder = new ArrayList<>();

        List<ExchangeTrade> exchangeTrades = new ArrayList<>();

        while (marketQueueIterator.hasNext()&&exitLoop){
            Map.Entry<BigDecimal,MergeOrder> markerOrderEntry = marketQueueIterator.next();
            BigDecimal makerPricer = markerOrderEntry.getKey();
            MergeOrder markerMergeOrder = markerOrderEntry.getValue();

            if(order.getOrderDirection()== OrderDirection.BUY.getCode() && order.getPrice().compareTo(makerPricer)<0){
                break;
            }



            if(order.getOrderDirection()== OrderDirection.SELL.getCode() && order.getPrice().compareTo(makerPricer)>0){
                break;
            }

            Iterator<Order> iterator = markerMergeOrder.iterator();

            while (iterator.hasNext()){
                Order marker = iterator.next();

                ExchangeTrade exchangeTrade =  processMatch(order,marker,orderBooks);
                exchangeTrades.add(exchangeTrade);
                if(order.isCompleted()){
                    completedOrder.add(order);
                    exitLoop = true;
                    break;
                }
                if(marker.isCompleted()){
                    completedOrder.add(marker);
                    iterator.remove();
                }


            }


            if (markerMergeOrder.size() == 0){
                    marketQueueIterator.remove();
            }



        }

        if (order.getAmount().compareTo(order.getTradedAmount())>0){
            orderBooks.addOrder(order);
        }




        if(exchangeTrades.size()>0)
        {
            handlerExchangeTrades(exchangeTrades);
        }

        if (completedOrder.size()>0){
            completedOrder(completedOrder);
            //发送盘口数据
            TradePlate tradePlate= order.getOrderDirection()==1?orderBooks.getBuyTradePlate(): orderBooks.getSellTradePlate();

            sendTradePlate(tradePlate);

        }


    }

    private void sendTradePlate(TradePlate tradePlate) {
        Message<TradePlate> build = MessageBuilder.withPayload(tradePlate).setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON).build();
        source.plateout().send(build);
    }

    private void completedOrder(List<Order> completedOrder) {
        Message<List<Order> > build = MessageBuilder.withPayload(completedOrder).setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON).build();
        source.completedordersout().send(build);
    }

    private void handlerExchangeTrades(List<ExchangeTrade> exchangeTrades) {

        Message<List<ExchangeTrade>> build = MessageBuilder.withPayload(exchangeTrades).setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON).build();
        source.exchangetradeout().send(build);

    }

    private ExchangeTrade processMatch(Order taker, Order marker,OrderBooks orderBooks) {

        BigDecimal dealPrice = marker.getPrice();
        BigDecimal turnoverAmount = BigDecimal.ZERO;
        BigDecimal needAmount = calcTradeAmount(taker);

        BigDecimal providerAmount = calcTradeAmount(marker);


        turnoverAmount = needAmount.compareTo(providerAmount)<=0 ? needAmount:providerAmount;

            if(turnoverAmount.compareTo(BigDecimal.ZERO) == 0){
                return null;
            }

            taker.setTradedAmount(taker.getTradedAmount().add(turnoverAmount));
            BigDecimal turnover = turnoverAmount.multiply(dealPrice).setScale(orderBooks.getCoinScale(), RoundingMode.HALF_UP);
            taker.setTurnover(turnover);

            marker.setTurnover(turnover);
            marker.setTradedAmount(marker.getTradedAmount().add(turnover));
            BigDecimal makerturnover = turnoverAmount.multiply(dealPrice).setScale(orderBooks.getCoinScale(), RoundingMode.HALF_UP);
                marker.setTurnover(makerturnover);

                    ExchangeTrade exchangeTrade = new ExchangeTrade();
                    exchangeTrade.setAmount(turnoverAmount);
                    exchangeTrade.setPrice(dealPrice);
                    exchangeTrade.setTime(System.currentTimeMillis());
                    exchangeTrade.setSymbol(orderBooks.getSymbol());

                    exchangeTrade.setDirection(OrderDirection.getOrderDirection(taker.getOrderDirection()));

                    exchangeTrade.setSellOrderId(marker.getOrderId());
                    exchangeTrade.setBuyOrderId(taker.getOrderId());
                    exchangeTrade.setBuyTurnover(taker.getTurnover());
                    exchangeTrade.setSellTurnover(marker.getTurnover());

                    if (taker.getOrderDirection() == OrderDirection.BUY.getCode()){
                        orderBooks.getBuyTradePlate().remove(marker,turnoverAmount);
                    }else {
                        orderBooks.getSellLimitPrice().remove(marker,turnoverAmount);
                    }
                    
                    return  exchangeTrade;

    }

    private BigDecimal calcTradeAmount(Order order) {

        return order.getAmount().subtract(order.getTradedAmount());

    }

    @Override
    public void afterPropertiesSet() throws Exception {
        MatchServiceFactory.addMatchService(MatchStrategy.LIMIT_PRICE,this::match);
    }
}
